#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QIntValidator>
#include <string.h>
#include <QMessageBox>
#include <QTime>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{

    ui->setupUi(this);
    ui->startIP_1->setMaxLength(3);
    ui->startIP_2->setMaxLength(3);
    ui->startIP_3->setMaxLength(3);
    ui->startIP_4->setMaxLength(3);
    ui->endIP_1->setMaxLength(3);
    ui->endIP_2->setMaxLength(3);
    ui->endIP_3->setMaxLength(3);
    ui->endIP_4->setMaxLength(3);

    memset(threadPool, 0, sizeof(threadPool));


    QObject::connect(ui->beginScan, &QPushButton::clicked, this, &MainWindow::scan);
    QObject::connect(ui->stopScan, &QPushButton::clicked, this, &MainWindow::terminateScan);
}

MainWindow::~MainWindow()
{
    delete ui;
}

bool checkIPRange(QString part) {
    return !(part.isEmpty() || part.toInt() < 0 || part.toInt() > 255);
}
bool checkPortRange(QString port) {
    return !(port.isEmpty() || port.toInt() < 0 || port.toInt() > 65535);
}

void MainWindow::terminateScan() {
    int confirm = QMessageBox::warning(
                this,
                tr("提示"),
                tr("您确定要提前结束扫描吗?"),
                QMessageBox::Ok, QMessageBox::Cancel);
    if(confirm != QMessageBox::Ok) {
        return ;
    }
    this->isOver = true;
    for(int i = 0; i < 64; i++) {
        if(threadPool[i] != NULL) {
//            threadPool[i]->terminate();
//            threadPool[i]->wait();
            threadPool[i]->requestInterruption();
            threadPool[i]->wait();
            qDebug() << "stop" << threadPool[i] << "\n";
        }
    }
    ui->resultArea->append("-------------------------------------------\n本次扫描已终止\n");
    qDebug() << "stopped\n";
}

void MainWindow::scan() {
    qDebug() << "开始扫描\n";
    qDebug() << "检查IP地址\n";
    if(!(   checkIPRange(ui->startIP_1->text()) &&
            checkIPRange(ui->startIP_2->text()) &&
            checkIPRange(ui->startIP_3->text()) &&
            checkIPRange(ui->startIP_4->text()) &&
            checkIPRange(ui->endIP_1->text()) &&
            checkIPRange(ui->endIP_2->text()) &&
            checkIPRange(ui->endIP_3->text()) &&
            checkIPRange(ui->endIP_4->text())
            )) {
        QMessageBox::warning(
                    this,
                    tr("提示"),
                    tr("请输入正确的IP地址!"),
                    QMessageBox::Ok);
        return ;
    }
    QString startIP = ui->startIP_1->text()
            .append('.').append(ui->startIP_2->text())
            .append('.').append(ui->startIP_3->text())
            .append('.').append(ui->startIP_4->text());
    QString endIP = ui->endIP_1->text()
            .append('.').append(ui->endIP_2->text())
            .append('.').append(ui->endIP_3->text())
            .append('.').append(ui->endIP_4->text());
    qDebug() << "start ip: " << startIP << ", end ip: " << endIP << "\n";
    if(startIP.left(startIP.lastIndexOf('.')) != endIP.left(endIP.lastIndexOf('.'))) {
        QMessageBox::warning(
                    this,
                    tr("提示"),
                    tr("请输入同网段的IP地址!"),
                    QMessageBox::Ok);
        return ;
    }
    if(startIP.right(startIP.length() - startIP.lastIndexOf('.') - 1).toInt() >
            endIP.right(endIP.length() - endIP.lastIndexOf('.') - 1).toInt()) {
        QMessageBox::warning(
                    this,
                    tr("提示"),
                    tr("请输入正确的IP范围!"),
                    QMessageBox::Ok);
        return ;
    }
    int ipCount = endIP.right(endIP.length() - endIP.lastIndexOf('.') - 1).toInt() -
            startIP.right(startIP.length() - startIP.lastIndexOf('.') - 1).toInt() + 1;
    qDebug() << "需要扫描" << ipCount << "个IP地址\n";

    qDebug() << "检查端口\n";
    if(!(checkPortRange(ui->startPort->text()) && checkPortRange(ui->endPort->text()))) {
        QMessageBox::warning(
                    this,
                    tr("提示"),
                    tr("请输入正确的端口号!"),
                    QMessageBox::Ok);
        return ;
    }
    int startPort = ui->startPort->text().toInt(), endPort = ui->endPort->text().toInt();
    if(startPort > endPort) {
        QMessageBox::warning(
                    this,
                    tr("提示"),
                    tr("请输入正确的端口范围!"),
                    QMessageBox::Ok);
        return ;
    }
    qDebug() << "start port: " << startPort << ", end port: " << endPort << "\n";
    int portCount = endPort - startPort + 1;
    qDebug() << "每个IP需要扫描" << portCount << "个端口\n";

    int totCount = ipCount * portCount;
    qDebug() << "总任务量: " << totCount << "\n";

    this->totTask = totCount;
    this->finTask = 0;
    this->openedPort.clear();
    this->isOver = false;

    qDebug() << "检查线程\n";
    int numOfThread = ui->threadCount->text().toInt();
    if(numOfThread == NULL || numOfThread < 0 || numOfThread > 10000) {
        QMessageBox::warning(
                    this,
                    tr("提示"),
                    tr("请输入正确的线程数(max=10000)!"),
                    QMessageBox::Ok);
        return ;
    }
    qDebug() << "设定线程数: " << numOfThread << "\n";

    qDebug() << "开始扫描...\n";

    QString currentIP;
    int currentPort;
    int scannedIP = 0;
    int scannedPort = 0;

    this->startTime =  QTime::currentTime();

    QString netAddr = startIP.left(startIP.lastIndexOf('.') + 1);
    int hostAddr = startIP.right(startIP.length() - startIP.lastIndexOf('.') - 1).toInt();

    if(numOfThread >= ipCount) {
        // 按线程分配，一个线程最多扫描一个IP
        int taskForSingleThread = totCount / numOfThread, dispatchTask = 0;
        for(int i = 0; i < numOfThread; i++) {
            // 计算出线程需要扫描的IP及端口
            currentIP = netAddr + QString::number(hostAddr + scannedIP, 10);
            currentPort = scannedPort % portCount + startPort;

            // 最后一个线程完成剩下的任务即可
            if(i == numOfThread - 1) taskForSingleThread = totCount - scannedPort;

            // 如果一个IP剩下的端口小于task，那么该线程少分配一些任务，扫描完当前IP即可
            // 而未分配到的任务将均分至剩下的线程中
            if(endPort - currentPort + 1 < taskForSingleThread) {
                dispatchTask = endPort - currentPort + 1;
            } else {
                dispatchTask = taskForSingleThread;
            }

            ScanThread* thread = new ScanThread(currentIP, 1, currentPort, dispatchTask);
            QObject::connect(thread, &ScanThread::scanOver, this, &MainWindow::updateResult);
            threadPool[i] = thread;
            thread->start();
            scannedPort += dispatchTask;
            scannedIP = scannedPort / portCount;

            // 修正平均工作量
            if((totCount - scannedPort) * 1.0 / (numOfThread - i - 1) > taskForSingleThread) {
                taskForSingleThread++;
            }
        }
    } else {
        // 按IP分配，每个线程扫描多IP
        int taskForSingleThread = ipCount / numOfThread;
        for(int i = 0; i < numOfThread; i++) {
            currentIP = netAddr + QString::number(hostAddr + scannedIP, 10);
            if(i == numOfThread - 1) {
                taskForSingleThread = ipCount - scannedIP;
            }
            ScanThread* thread = new ScanThread(currentIP, taskForSingleThread, startPort, portCount);
            QObject::connect(thread, &ScanThread::scanOver, this, &MainWindow::updateResult);
            threadPool[i] = thread;
            thread->start();
            scannedIP += taskForSingleThread;

            if((ipCount - scannedIP) * 1.0 / (numOfThread - i - 1) > taskForSingleThread) {
                taskForSingleThread++;
            }
        }
    }
}

void* MainWindow::scan_some_port(void *arg) {
    return NULL;
}

void MainWindow::updateResult(QString info, bool opened) {
    if(this->isOver) return ;
    ui->resultArea->append(info + " " + (opened ? "opened" : "closed"));
    if(opened) {
        this->openedPort.append(info);
    }
    this->finTask++;
    if(this->finTask == this->totTask) { // 所有任务完成
        this->endTime = QTime::currentTime();
        ui->resultArea->append("扫描结束，耗时: " + QString::number(this->startTime.msecsTo(this->endTime) / 1000.0));
        this->showAllOpenedPorts();
    }
}

void MainWindow::showAllOpenedPorts() {
    ui->resultArea->append("-------------------------------------------\n开启的IP及端口号如下");
    int numOfOpenedPort = this->openedPort.size();
    for(int i = 0; i < numOfOpenedPort; i++) {
        ui->resultArea->append(this->openedPort.at(i));
    }
    ui->resultArea->append("-------------------------------------------\n本次扫描完成\n");
    qDebug() << "over";
}




